import {forEach, isEmpty, find, mergeWith, isArray, keys, isNil, isObject} from 'lodash-es';
import {ShallowObjectType} from '../hooks/util';
import {UnionSpecItem, EnumValuesSpecItem, AlternativeSpecItem, ObjectSpecItem, ListSpecItem} from '@jsw/report-core';

interface SpecDiffType {
    path: string;
    spec: ShallowObjectType<any>;
}

const getSpecItemByPath = (
    spec: UnionSpecItem[] | EnumValuesSpecItem[] = [],
    pathArr: string[] = []
): UnionSpecItem | undefined => {
    const [first, ...rest] = pathArr;
    if (!first) {
        return undefined;
    }
    const result = find(spec, (item: UnionSpecItem | EnumValuesSpecItem) => {
        if ((item as UnionSpecItem).dataType) {
            const {dataType} = item as UnionSpecItem;
            if (dataType === 'LIST' && /\[\*\]/.test(first)) {
                const currentName = first.replace(/\[\*\]/, '');
                return currentName === (item as UnionSpecItem).name;
            }
            return (item as UnionSpecItem).name === first;
        }
        return (item as EnumValuesSpecItem).value === first;
    });
    if (result && rest.length) {
        const objectResult = result as AlternativeSpecItem | ListSpecItem | ObjectSpecItem;
        const {dataType} = objectResult;
        if (dataType !== 'ALTERNATIVE') {
            return getSpecItemByPath(objectResult.items, rest);
        }
        return getSpecItemByPath(objectResult.enumValues, rest);
    } else if (result && !rest.length) {
        return result as UnionSpecItem;
    }
    return undefined;
};

const cutObjectEmptyKey = (obj: ShallowObjectType<any>) => {
    const objKeys = keys(obj);
    forEach(objKeys, (key) => {
        if (isNil(obj[key]) || obj[key] === -1 || obj[key] === '') {
            delete obj[key];
        } else if (isObject(obj[key])) {
            cutObjectEmptyKey(obj[key]);
            if (isEmpty(obj[key])) {
                delete obj[key];
            }
        }
    });
};

export const mergeSpecWithDiff = (originSpec: UnionSpecItem[] = [], diffArray: SpecDiffType[] = []) => {
    forEach(diffArray, (item) => {
        if (item.path && item.spec && !isEmpty(item.spec)) {
            const specItem = getSpecItemByPath(originSpec, item.path.split('.'));
            if (specItem) {
                mergeWith(specItem, item.spec, (objValue, srcValue) => {
                    if (isArray(objValue)) {
                        return srcValue;
                    }
                });
                cutObjectEmptyKey(specItem);
            }
        }
    });
    return originSpec;
};
